package org.example;

import lombok.Data;
import org.example.bean.User;
import org.example.biz.UserBiz;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Company 源辰信息
 *
 * @author navy
 * @date 2024/4/15
 * Email haijunzhou@hnit.edu.cn
 */
@Data
public class MyAnnotationConfigApplicationContext {

    private Class configClass;
    private Object configBean;

    private Map<String,BeanDefinition> beanDefinitionMap = new HashMap<>();
    private Map<String,Object> singletonObjects = new HashMap<>();

    private File classesRootDir;

    public MyAnnotationConfigApplicationContext(Class configClass)
            throws InstantiationException,  // 构造函数调用失败
            IllegalAccessException, UnsupportedEncodingException {        // 访问作用域错误， 例如： private
        this.configClass = configClass;

        Annotation configClassAnnotation = configClass.getAnnotation(Configuration.class);
        if(configClassAnnotation == null){
            throw new RuntimeException("该类不是 Spring 配置类");
        }

        // 创建配置类对象
        configBean = configClass.newInstance();

        // bean 方法
        Method[] methods = configClass.getDeclaredMethods(); // 获取定义的方法
        for (Method method : methods) {
            Bean bean = method.getAnnotation(Bean.class);
            if (bean==null) {
                continue;
            }
            // 读取 bean 定义信息
            BeanDefinition bd = new BeanDefinition();

            // 名字  bean.value, bean.name  method.name
            String name;
            if(bean.value().length>0){
                name = bean.value()[0];
            } else if(bean.name().length>0){
                name = bean.name()[0];
            } else {
                name = method.getName();
            }
            bd.setName(name);
            // 方法的返回值类型， 就是 bean 的类型
            bd.setBeanClass(method.getReturnType());
            bd.setBeanMethod(method);
            Scope scope = method.getAnnotation(Scope.class);
            if(scope!=null){
                bd.setScope(scope.value());
            } else {
                bd.setScope("singleton");
            }
            // @Primary 请自行扩展完成
            Primary primary = method.getAnnotation(Primary.class);
            if(primary!=null){
                bd.setPrimary(true);
            }
            beanDefinitionMap.put(name,bd);
        }

        // 包扫描
        // 扫描编译后的文件目录， classes

        // 类加载器
        ClassLoader classLoader = this.getClass().getClassLoader();
        // 获取指定包的 url 地址  “” 类路径根目录
        URL url = classLoader.getResource("");
        System.out.println("url.getPath() = " + url.getPath());
        String path = url.getPath();
        // 将 url 解码
        path = URLDecoder.decode(path, "utf-8");
        System.out.println("url.getPath() = " + path);
        classesRootDir = new File(path);
        List<File> classFiles = new ArrayList<>();

        // 获取包扫描路径
        ComponentScan scan = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
        for (String packagePath : scan.value()) {
            url = classLoader.getResource(packagePath.replaceAll("\\.","/"));
            path = url.getPath();
            path = URLDecoder.decode(path, "utf-8");
            System.out.println("packagePath =" + path);
            // 递归扫描方法
            // 收集所有的类
            findClassFile(new File(path), classFiles);
        }

        // 检查扫描到的类文件
        classFiles.forEach(System.out::println);

//        List<Class> classList = new ArrayList<>();
        classFiles.forEach(file->{
            // 通过反射获取组件信息 beanDef....
            String classPath = file.getAbsoluteFile().toString();
            // 替换根目录
            classPath = classPath.replace(classesRootDir.getAbsolutePath(),"");
            // 截取 。class 后缀
            classPath = classPath.substring(0, classPath.length() - ".class".length());
            // 替换 \ 为 .
            classPath = classPath.replaceAll("\\\\", ".");
            // 去掉第一个 /
            classPath = classPath.substring(1);
            // 将 / 替换成 .
            classPath = classPath.replaceAll("/",".");
            try {
                Class cls = Class.forName(classPath);
                Component component = (Component) cls.getAnnotation(Component.class);
                Service service = (Service) cls.getAnnotation(Service.class);
                Repository repository = (Repository) cls.getAnnotation(Repository.class);
                Controller controller = (Controller) cls.getAnnotation(Controller.class);
                if(component==null && service==null && repository==null && controller==null ){
                    return;
                }
                // 读取 bean 定义信息
                BeanDefinition bd = new BeanDefinition();

                // 名字  bean.value, bean.name  method.name
                String name=null;

                if(component!=null){
                    name = component.value();
                } else if(service!=null){
                    name = service.value();
                } else if(repository!=null){
                    name = repository.value();
                } else if(controller!=null){
                    name = controller.value();
                }
                if("".equals(name)){
                    // 没有设置name ，类名首字母小写
                    name = cls.getSimpleName().substring(0,1).toLowerCase()
                            + cls.getSimpleName().substring(1);
                }


                bd.setName(name);
                // 方法的返回值类型， 就是 bean 的类型
                bd.setBeanClass(cls);

                Scope scope = (Scope) cls.getAnnotation(Scope.class);
                if(scope!=null){
                    bd.setScope(scope.value());
                } else {
                    bd.setScope("singleton");
                }
                // @Primary 请自行扩展完成
                Primary primary = (Primary) cls.getAnnotation(Primary.class);
                if(primary!=null){
                    bd.setPrimary(true);
                }
                beanDefinitionMap.put(name,bd);

            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        });

        // 根据扫描结果 BeanDefinition 创建 bean
        // 将 bean 存入容器
        beanDefinitionMap.forEach((name,bd)->{
            System.out.println(name  + " => " + bd);
            if (bd.getBeanMethod()!=null) {
                // Bean 方法 创建对象
                // 只处理无参数的 bean 方法
                if(bd.getBeanMethod().getParameterCount()==0){
                    try {
                        Object bean = bd.getBeanMethod().invoke(configBean);
                        singletonObjects.put(bd.getName(), bean);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    } catch (InvocationTargetException e) {
                        // bean方法执行异常， 方法内部出错
                        throw new RuntimeException("bean方法执行异常， 方法内部出错",e);
                    }
                }
            } else {
                // 组件扫描，创建对象
                try {
                    Object bean = bd.getBeanClass().newInstance();
                    singletonObjects.put(bd.getName(),bean);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        });

        // 实现自动装配（依赖注入） autowire
        singletonObjects.values().forEach(bean->{
            for (Field field : bean.getClass().getDeclaredFields()) {
                // Autowired  默认 byType + byName
                if(field.getAnnotation(Autowired.class) != null){
                    Qualifier qualifier = field.getAnnotation(Qualifier.class);
                    Object fieldBean;
                    if(qualifier!=null){
                        // byName
                        fieldBean = getBean(qualifier.value());
                    } else {
                        fieldBean = getBean(field.getType());//byType
                    }
                    try {
                        field.setAccessible(true);
                        field.set(bean, fieldBean);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
                // TODO 扩展实现 Resource   默认 byName + byType  坑
//                @Resource
//                UserDao userDao;
                // field.getName(); //  java 反射获取不到 变量名
            }
        });


    }

    // 递归扫描方法
    private void findClassFile(File dir, List<File> classFiles) {
        // 过滤 字节码文件
        File[] files = dir.listFiles(ff -> ff.getName().endsWith(".class"));
        for (File file : files) {
            // 将文件存入集合
            classFiles.add(file);
        }

        File[] subDirs = dir.listFiles(ff -> ff.isDirectory());
        for (File subDir : subDirs) {
            findClassFile(subDir, classFiles);
        }

//        dir.isFile();
//        dir.isDirectory();

        // File abcd = new File("abcd");
       /* abcd.mkdir()
                abcd.createNewFile()     */


    }

    public Object getBean(String name){
        Object bean = singletonObjects.get(name);
        if(bean == null){
            throw  new RuntimeException("没有该bean：" + name);
        }
        return bean;
    }

    public <T> T getBean(Class<T> cls){
        // 多个同类型的beaqn =》 primary
        // java stream 流式编程
        // 双列集合 key， value
        List<Map.Entry<String, BeanDefinition>> list = beanDefinitionMap
                .entrySet() // 转单列集合 set
                .stream()   // 转流对象 stream
                .filter(entry -> entry.getValue().getBeanClass().equals(cls))   //  过滤
                .collect(Collectors.toList());  // 收集结果保存到list集合
        if(list.isEmpty()){
            throw new RuntimeException("没有该bean：" + cls);
        } else if(list.size() == 1){
            String beanName = list.get(0).getKey();
            return (T) singletonObjects.get(beanName);
        } else {
            for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : list) {
                if (beanDefinitionEntry.getValue().isPrimary()) {
                    String beanName = beanDefinitionEntry.getKey();
                    return (T) singletonObjects.get(beanName);
                }
            }
        }
        throw new RuntimeException("没有该bean：" + cls);
    }

    public static void main(String[] args) throws Exception {
        MyAnnotationConfigApplicationContext cxt =
                new MyAnnotationConfigApplicationContext(BeanConfig.class);

        User user0 = (User) cxt.getBean("user0");
        System.out.println("user0 = " + user0);

        Object userAction1 = cxt.getBean("userAction1");
        System.out.println("userAction1 = " + userAction1);

        User user = cxt.getBean(User.class);
        System.out.println(user);


        UserBiz userBiz = (UserBiz) cxt.getBean("ubiz");
        System.out.println("userBiz.getUserDao() = " + userBiz.getUserDao());
        System.out.println("userBiz.getUserDao1() = " + userBiz.getUserDao1());
    }
}
